package flca.voicenav;

import java.io.File;
import java.util.Date;
import java.util.Locale;

import android.app.Notification;
import android.app.Service;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;
import android.util.Log;
import android.widget.Toast;

import com.google.android.maps.GeoPoint;

import flca.android.util.LocationMgr;
import flca.android.util.SoundMgr;
import flca.voicenav.model.GpxRoute;
import flca.voicenav.model.TracePoints;
import flca.voicenav.model.TracePt;
import flca.voicenav.model.VoiceNavData;
import flca.voicenav.util.DistHelper;
import flca.voicenav.util.FileUtils;
import flca.voicenav.util.NotifyUserHelper;
import flca.voicenav.util.TracePtHelper;

public class NavigateBackgroundTask extends Service
		implements LocationListener, VoiceNavConstants, OnInitListener
{
	private TracePoints mTracePoints = new TracePoints();
	private NotifyUserHelper mNotifyHelper;
	private TextToSpeech mTts;
	private int mLostTrackCount = 0;
	private int mInformLostTrackCount = 0;
	
	private StringBuffer sb1 = new StringBuffer();
	
//    private final IBinder mBinder = new LocalBinder();

	public NavigateBackgroundTask()
	{
		super();
	}

    @Override
    public void onCreate() 
    {
		Toast.makeText(this, "Navigation Service Created", Toast.LENGTH_LONG).show();
		Log.d(TAG, "onCreate");
		mTts = new TextToSpeech(getApplicationContext(),this); 
		this.mNotifyHelper = new NotifyUserHelper(mTts);
		showNotification();
    }

	@Override
	public void onStart(Intent intent, int startid) {
		Log.i(TAG, "service onStart");
	}
	
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) 
    {
    	say("service started");
        Log.i(TAG, "Received start id " + startId + ": " + intent);
        LocationMgr.getinstance().addListener(this);
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, "service stopped");
        writeToFile();
        LocationMgr.getinstance().removeListener(this);
		say("service stopped");
    	stopForeground(true);
    }

    @Override
    public IBinder onBind(Intent intent) {
    	return null;//mBinder;
    }

	@Override
	public void onInit(int status)
	{
		if (status == TextToSpeech.SUCCESS && mTts != null) {
			mTts.setLanguage(Locale.UK);
		}
	}
    /**
     * Show a notification while this service is running.
     */
    private void showNotification() 
    {
    	Notification notification = new Notification();
    	startForeground(R.string.local_service_started, notification);
    	// Set the icon, scrolling text and timestamp
    }

	private void processLocation(Location aLocation)
	{
		mTracePoints.add(new TracePt(aLocation));
				
		TracePtHelper.processLocation(mTracePoints, getGpxRoute());
		if (TracePtHelper.isLastPointValid(mTracePoints)) {
			mLostTrackCount = 0; mInformLostTrackCount = 0;
			informIfNeeded();
		} else {
			processLostTrack();
		}
		
		updateInfoMsg();
	}

	private void updateInfoMsg()
	{
		String s = "afstand tot route = " + mTracePoints.last().getMinDist();
		s += "\n nr=" + mTracePoints.size() + " segm=" + mTracePoints.last().getSegmIndex();
		VoiceNavData.setInfoMsg(s);
	}

	private void processLostTrack()
	{
		Log.w(TAG,"nomatch dist ");
		mLostTrackCount++;
		if (mLostTrackCount > MAX_LOST_TRACK_CNT) {
			if (movingAwayFromFinish()) {
				informLostTrack();
			}
		}
	}

	private void informLostTrack()
	{
		//TODO houd bij wanneer de vorige werd gewaarschuwd, mbv LostTrackInfoHelper;
		if (mInformLostTrackCount > 1) {
			SoundMgr.getInstance().playSound(SND_ICQ);
			say("I lost track");
			mInformLostTrackCount++;
		}
	}

	private boolean movingAwayFromFinish() 
	{
		GeoPoint lastPt = mTracePoints.last().geoPoint();
		GeoPoint olderPt = mTracePoints.get(mTracePoints.size() - MAX_LOST_TRACK_CNT + 1).geoPoint();
		double currDist = TracePtHelper.getDistanceToFinish(lastPt, getGpxRoute(), mTracePoints.prev().getDirection());
		double oldDist = TracePtHelper.getDistanceToFinish(olderPt, getGpxRoute(), mTracePoints.prev().getDirection());
		return currDist > oldDist;
	}
	
	private void informIfNeeded()
	{
		mNotifyHelper.update(mTracePoints.last());
		int dist = mTracePoints.last().getNextDist();
		if (TracePtHelper.nearTheEnd(mTracePoints, getGpxRoute(), notifyDistance())) {
			sb1.append(now() + ";inform finish " + dist + "\n");
			mNotifyHelper.informFinish(dist);
		} else if (dist < notifyDistance()) {
			sb1.append(now() + ";inform " + mTracePoints.last().getNextCorner() + ";" + 
					getGpxRoute().getTrkPt(mTracePoints.last().getNextCorner()).getAngle() + ";" + dist + "\n");
			mNotifyHelper.inform(dist);
		}
	}

	@Override
	public void onLocationChanged(Location aLoc)
	{
		dumpLocation(aLoc); 
		processLocation(aLoc);
	}

	@Override
	public void onProviderDisabled(String arg0)
	{
	}

	@Override
	public void onProviderEnabled(String arg0)
	{
	}

	@Override
	public void onStatusChanged(String arg0, int status, Bundle arg2)
	{
	}

	/**
	 * return the distance when we should inform user
	 * 30 km/uur ~ 8 m/s
	 * @return
	 */
	private int notifyDistance()
	{
		int r =  (int) (5 * DistHelper.calcSpeed(mTracePoints));
		Log.i(TAG, "notify distance " + r);
		if (r < MIN_NOTIFY_DIST) {
			r = MIN_NAVPNT_DIST; //walking speed
		} else if (r > MAX_NOTIFY_DIST) {
			r = MAX_NOTIFY_DIST;
		}
		
		return r;
	}

	private void say(String msg)
	{
		if (!VoiceNavData.isEndClicked()) {
			mTts.speak(msg, TextToSpeech.QUEUE_FLUSH, null);
		}
	}

	private GpxRoute getGpxRoute() {
		return VoiceNavData.getGpxRoute();
	}
	
    /**
     * Class for clients to access.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with
     * IPC.
     */
    public class LocalBinder extends Binder {
        NavigateBackgroundTask getService() {
            return NavigateBackgroundTask.this;
        }
    }
    
    //--- debug utils
    
    int cnt = 0;
	private void dumpLocation(Location loc)
	{
		if (cnt++ % 100 == 0) {
			SoundMgr.getInstance().playSound(SND_HIGH_SIMPLE);
		}
		
		String s1 = now() + ";" + (int) loc.getAccuracy() + ";" + (int) loc.getBearing() + ";" + 
				loc.getSpeed() + ";" + loc.getLatitude() + ";" + loc.getLongitude() + "; " ;
		if (mTracePoints.size() > 2) {
			s1 += mTracePoints.prev().toString();
		}
		
		sb1.append( s1 + "\n");
		Log.i(TAG, s1);
	}
	
	private String now() {
		String now = "" + new Date().getTime();
		now = now.substring(now.length() - 5);
		return now;
	}

    private void writeToFile()
    {
    	try {
    		File sdCard = Environment.getExternalStorageDirectory();
    		File dir = new File (sdCard.getAbsolutePath() + "/voicenav/log");
			File file1 = new File(dir, "locations.txt");
			FileUtils.saveFile(file1, sb1.toString());
		} catch (Exception e) {
			Log.e(TAG, "writeToFile" + e);
		}
    }

	//----------- shortcuts ------------
//	private GeoPoint geoPoint(Location aLocation) {
//		return geoPoint(aLocation.getLatitude(), aLocation.getLongitude());
//	}
//	private GeoPoint geoPoint(double lat, double lon) {
//		return new GeoPoint((int) (lat * 1E6), (int) (lon * 1E6));
//	}
}
